package utils

import (
	"bytes"
	"crypto/md5"
	"encoding/gob"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"parse/src/configs"
	"parse/src/events/termbox-go"
	"parse/src/globals"
	"parse/src/token"
	"reflect"
	"strings"
)

// 判断 在items中是否存在 item
func IsContain(items []interface{}, item interface{}) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

// 判断 在items中是否存在 item
func IsContainMapString(items map[string]interface{}, item interface{}) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

// 判断 在items中是否存在 item
func IsContainMap(items []interface{}, key string, value interface{}) bool {
	for _, eachItem := range items {
		if value != nil && eachItem != nil && reflect.DeepEqual(map[string]string{
			key: value.(string),
		}, eachItem) {
			return true
		}
	}
	return false
}

// 获取到item元素的属性
func GetItemAttribute(item interface{}, attribute string) interface{} {
	name := reflect.ValueOf(item).Elem().FieldByName(attribute)
	if name.Kind() == reflect.Invalid { // 表示得到的属性没有值
		return nil
	} else {
		return name.Interface()
	}
}

// 判断 other是否是 obj的实例
func IsInstance(other interface{}, typeName string) bool {
	ty := reflect.TypeOf(other).String()
	return ty == "*interpreter."+typeName
}

// 运行函数 fun 的 methodName 函数，参数为a,b,c ，注意，此处的fun 必须是函数的地址
// 利用反射的方式，得到需要运行的函数
func RunFunc(fun interface{}, methodName string, params_ ...interface{}) interface{} {
	// 利用反射的方式，得到需要运行的函数
	mtV := reflect.ValueOf(fun).Elem()
	// 得到参数
	var params []reflect.Value
	for _, v := range params_ {
		params = append(params, reflect.ValueOf(v))
	}
	name := mtV.MethodByName(methodName)
	if name.Kind() == reflect.Invalid {
		return nil
	}
	call := name.Call(params)
	if len(call) > 0 {
		return call[0].Interface()
	}
	return nil
}

// 拿到参数所有名字
func GetAttributeNames(fun interface{}, method string) []string {
	// 利用反射的方式，得到需要运行的函数
	//mtV := reflect.ValueOf(fun).MethodByName(method)
	//reflect.ValueOf(T).MethodByName("TMethod").Type().In(0).Elem().Name()
	return nil
}

// 运行函数 fun 的 methodName 函数，参数为a,b,c ，注意，此处的fun 必须是函数的地址
// 利用反射的方式，得到需要运行的函数
func RunFuncOther(fun interface{}, methodName string, params_ ...interface{}) interface{} {
	// 利用反射的方式，得到需要运行的函数
	mtV := reflect.ValueOf(fun)
	// 得到参数
	var params []reflect.Value
	for _, v := range params_ {
		params = append(params, reflect.ValueOf(v))
	}
	name := mtV.MethodByName(methodName)
	if name.Kind() == reflect.Invalid {
		return nil
	}
	call := name.Call(params)
	if len(call) > 0 {
		return call[0].Interface()
	}
	return nil
}

// 得到节点的名字
func GetTypeName(node interface{}, bool_ bool) string {
	if bool_ {
		t := strings.SplitAfter(reflect.TypeOf(node).String(), ".")
		return t[len(t)-1]
	}
	return reflect.TypeOf(node).String()
}

//生成32位md5字串
func Md5(s interface{}) string {
	h := md5.New()
	h.Write([]byte(s.(string)))
	return hex.EncodeToString(h.Sum(nil))
}

// 有箭头的字符串错误提示
func StringWithArrows(text string, posStart *token.Position, posEnd *token.Position) string {

	result_ := ""
	// 计算索引
	idxStart := int(math.Max(float64(strings.LastIndex(text[:posStart.Idx], "\n")), 0))
	idxEnd := strings.Index(text[idxStart+1:], "\n")

	// 得到字符串的长度
	if idxEnd < 0 {
		idxEnd = len(text)
	}

	lineCount := posEnd.Ln + 1 - posStart.Ln
	for i := 0; i < lineCount; i++ {
		// 计算行数
		var line string
		if idxEnd > idxStart {
			line = text[idxStart:idxEnd]
		} else {
			line = text[idxStart : idxStart+idxEnd]
		}

		var colStart int
		if i == 0 {
			colStart = posStart.Col
		} else {
			colStart = 0
		}
		var colEnd int
		if i == lineCount-1 {
			colEnd = posEnd.Col
		} else {
			colEnd = len(line) - 1
		}

		// 把行数加到结果上
		result_ = line + "\n"
		for i := 0; i < colStart; i++ {
			result_ += " "
		}
		for i := 0; i < colEnd-colStart; i++ {
			result_ += "^"
		}

		// 重新计算索引
		idxStart = idxEnd
		idxEnd = strings.Index(text[idxStart:], "\n")
		if idxEnd < 0 {
			idxEnd = len(text)
		}
	}
	return strings.Replace(result_, "\t", "", 0)
}

// 深度复制
func DeepCopy(dst, src interface{}) error {
	var buf bytes.Buffer
	if err := gob.NewEncoder(&buf).Encode(src); err != nil {
		return err
	}
	return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}

// 打印函数
func Print(s interface{}) {
	switch configs.GetConfig("TESTTYPE") {
	case 1: // 简单版
		fmt.Print(s)
	case 2: // 线上版
		s := fmt.Sprintf("%v", s)
		globals.EditBoxObj.CursorLineOffset++
		_, l := termbox.Size()
		if globals.EditBoxObj.CursorLineOffset >= l {
			globals.EditBoxObj.CursorLineOffset = l - 1
			termbox.MoveUp(1)
		}
		termbox.Tbprint(0, globals.EditBoxObj.CursorLineOffset, termbox.ColorDefault, termbox.ColorDefault, s)
	}
}

// 打印函数 换行
func Println(s interface{}) {
	switch configs.GetConfig("TESTTYPE") {
	case 1: // 简单版
		fmt.Println(s)
	case 2: // 线上版
		s := fmt.Sprintf("%v", s)
		globals.EditBoxObj.CursorLineOffset++
		_, l := termbox.Size()
		if globals.EditBoxObj.CursorLineOffset >= l {
			globals.EditBoxObj.CursorLineOffset = l - 1
			termbox.MoveUp(1)
		}
		termbox.Tbprint(0, globals.EditBoxObj.CursorLineOffset, termbox.ColorDefault, termbox.ColorDefault, s)
	}
}

// 打印函数 返回
func PrintRet(s interface{}) interface{} {
	switch configs.GetConfig("TESTTYPE") {
	case 1: // 简单版
		_ = fmt.Sprintf("%v", s)
		return s
	case 2: // 线上版
		s := fmt.Sprintf("%v", s)
		globals.EditBoxObj.CursorLineOffset++
		termbox.Tbprint(0, globals.EditBoxObj.CursorLineOffset, termbox.ColorDefault, termbox.ColorDefault, s)
		return s
	}
	return nil
}

// 打印函数 换行 返回
func PrintlnRet(s interface{}) interface{} {
	switch configs.GetConfig("TESTTYPE") {
	case 1: // 简单版
		_ = fmt.Sprintf("%v\n", s)
		return s
	case 2: // 线上版
		s := fmt.Sprintf("%v", s)
		globals.EditBoxObj.CursorLineOffset++
		termbox.Tbprint(0, globals.EditBoxObj.CursorLineOffset, termbox.ColorDefault, termbox.ColorDefault, s)
		return s
	}
	return nil
}

// 打印错误
func PrintErr(s string) {
	sArr := strings.Split(s, "\n")
	for _, item := range sArr {
		Println(item)
	}
}

// 读取文件
func ReadFile(name string) string {
	// 得到当前的文件夹
	str, _ := os.Getwd()
	path := str + "/" + name
	f, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println("读取文件失败，请确定文件存在", err)
	}
	return string(f)
}
